#include "ThreadPool.h"
#include <iostream>

// 构造函数 
ThreadPool::ThreadPool(size_t threads) {
    std::cout << "线程池构造函数被调用，创建 " << threads << " 个工作线程" << std::endl;
    
    for(size_t i = 0; i < threads; ++i){
        // 下面的代码等价于：this->workers.push_back(std::thread([this]{this->workerThread};));
        this->workers.emplace_back(
            [this]{this->workerThread();}
        );
    }

    std::cout << "所有工作线程创建完成" << std::endl;
}

// 析构函数
ThreadPool::~ThreadPool() {
    std::cout << "线程池开始关闭" << std::endl;

    if(!this->stop){
        std::unique_lock<std::mutex> lock(queue_mutex);
        if(!this->stop){
            stop = true;
        }
    }

    this->condition.notify_all();

    for(std::thread& work : this->workers){
        if(work.joinable()){
            work.join();
        }
    }

    std::cout << "线程池已关闭" << std::endl;
}

// 工作线程函数 - 线程池的核心逻辑
void ThreadPool::workerThread(){
    while(true){
        // step_1: 获取线程
        std::function<void()> task;

        {
            // 先竞争锁，抢到锁的去条件变量下等
            std::unique_lock<std::mutex> lock(this->queue_mutex);
            this->condition.wait(lock,[this](){
                // 下面行代码的逻辑为：
                // 如果线程停止了，直接向后执行
                // 如果线程池没有停止，线程就要等到任务队列中有任务在继续执行
                // 这样写可以避免伪唤醒
                return this->stop || !tasks.empty(); 
            });

            // 线程出来之后，也不要着急获取任务，得看看情况
            // 情况一: 线程退出的情况 —— 线程池停止&&任务队列中无任务
            if(this->stop && this->tasks.empty()){
                return;
            }
            // 情况二: 线程继续执行的情况
            // 继续执行的情况分为三种子情况
            // 1.线程池停止&&任务队列中有任务
            // 2.线程池未停&&任务队列中有任务
            // 3.线程池未停&&任务队列中无任务 —— 线程池未停，即使没有任务也不能退出（这种情况由谓词保证了不会发生）

            // 开始获取任务喽
            if(!tasks.empty()){
                task = std::move(this->tasks.front());
                this->tasks.pop();
            }
        }

        // step_2: 执行任务 
        // 在锁外面执行任务，线程从任务队列中获取任务必须是串行的
        // 但是执行任务可以是并行的，并且，这样可以避免阻塞其他线程，提高线程池的效率
        if(task){
            try{
                task();
            }
            catch(std::exception& e){
                std::cout << "任务执行异常" << e.what() << std::endl;
            }
            catch(...){
                std::cout << "任务执行未知异常" << std::endl;
            }
        }
    }
}

bool ThreadPool::isStopped()
{
    return this->stop;
}
